package org.as4game.scene3d
{
	import flash.geom.Matrix;
	import flash.geom.Matrix3D;
	import flash.geom.Vector3D;
	
	/**
	 * 摄像机
	 */
	public class Camera extends MovableObject
	{
		private var _aspect : Number; 			// 纵横比
		private var _fovDegrees : Number;		// 视角
		private var _zNear : Number;
		private var _zFar : Number;
		private var _projection:Matrix3D;		// 透视矩阵 
		
		/**
		 * 初始化默认的相机属性
		 */
		public function Camera(name:String=null){
			super(name);
			
			_aspect = 1;
			_fovDegrees = 70;
			_zNear = 0.02;
			_zFar = 1000;
			
			_updateProjectionMatrix();	
		}
		
		/**
		 * 透视投影矩阵
		 */
		public function get projection() : Matrix3D {
			if( ! _projection ){
				_updateProjectionMatrix();
			}
			return _projection;
		}
		
		/**
		 * 纵横比, width/height
		 */
		public function get aspect():Number{
			return _aspect;
		}
		public function set aspect(value:Number):void{
			if( _aspect != value ){
				_aspect = value;
				_projection = null;
			}
		}
		
		/**
		 * 视角
		 */
		public function get fovDegrees():Number{
			return _fovDegrees;
		}
		public function set fovDegrees(value:Number):void{
			if( _fovDegrees != value ){
				_fovDegrees = value;
				_projection = null;
			}
		}
		
		/**
		 * z-far
		 */
		public function get zFar():Number{
			return _zFar;
		}
		public function set zFar(value:Number):void{
			if( _zFar != value ){
				_zFar = value;
				_projection = null;
			}
		}
		
		/**
		 * z-near
		 */
		public function get zNear():Number{
			return _zNear;
		}
		public function set zNear(value:Number):void{
			if( _zNear != value ){
				_zNear = value;
				_projection = null;
			}
		}
		
		/**
		 * 更新投影矩阵
		 */
		private function _updateProjectionMatrix ( ) : void {
			var yval:Number = _zNear * Math.tan( _fovDegrees * Math.PI / 360.0);
			var xval:Number = yval * _aspect;	
			_projection = makeFrustumMatrix(-xval, xval, -yval, yval, _zNear, _zFar);
		}
		
		/**
		 * 构造正交投影变化矩阵
		 */
		static public function makeRectangularProjection( left:Number, right:Number, top:Number, bottom:Number, zNear:Number, zFar:Number ):Matrix3D{
			var mat:Matrix3D = new Matrix3D;
			
			var width:Number = right - left;
			var height:Number = bottom - top;
			var deep:Number = zFar - zNear;
			
			// 把 [left, right] 变化到 [-1, 1]
			mat.appendTranslation( -left, -top, -zNear );
			mat.appendScale( 2 / width, 2 / height, 1/deep );
			mat.appendTranslation( -1, -1, 0);
			mat.appendScale(1, -1, 1);
			
			return mat;
		} 
		
		/**
		 * 构造锥体变化矩阵
		 */
		static public function makeFrustumMatrix ( left : Number, right : Number, top : Number, bottom : Number, zNear : Number, zFar : Number  ) : Matrix3D {
			var rval : Matrix3D = new Matrix3D;
			var myrawData : Vector.<Number> = new Vector.<Number>(16);
			
			myrawData[0] = (2*zNear)/(right-left);
			myrawData[1] = 0;
			myrawData[2] = (right+left)/(right-left);
			myrawData[3] = 0;
			
			myrawData[4] = 0;	
			myrawData[5] = (2*zNear)/(top-bottom);		
			myrawData[6] = (top+bottom)/(top-bottom);		
			myrawData[7] = 0;
			
			myrawData[8] = 0;
			myrawData[9] = 0;
			myrawData[10]= zFar/(zNear-zFar);	 
			myrawData[11]= -1;		
			
			myrawData[12]=  0;
			myrawData[13]=  0;
			myrawData[14]=  (zNear*zFar)/(zNear-zFar);	
			myrawData[15]=	0;
			
			rval.rawData = myrawData;	
			return rval;
		}		
	}
}